package com.yupi.oj.service.impl;

import com.yupi.oj.common.ErrorCode;
import com.yupi.oj.exception.BusinessException;
import com.yupi.oj.mapper.QuestionMapper;
import com.yupi.oj.mapper.QuestionRecommendMapper;
import com.yupi.oj.model.entity.Question;
import com.yupi.oj.model.entity.QuestionRecomend;
import com.yupi.oj.service.RecommendService;
import lombok.extern.slf4j.Slf4j;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author FengHuan Wang
 * @Date 2025/3/13 14:14
 * @Version 1.0
 */
@Service
@Slf4j
public class RecommendServiceImpl implements RecommendService {

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private QuestionRecommendMapper questionRecommendMapper;

    /**
     * 推荐题目，返回题目id
     * @param userId
     * @return
     */
    @Override
    public List<Integer> getRecommendId(Long userId) {
        // 1、先向数据库查询所有的提交题目列表
        List<QuestionRecomend> questionRecomendList = questionRecommendMapper.getQuestion();
        if (questionRecomendList == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "提交题目位空");
        }
        // 2、再将数据处理成csv文件
        String property = System.getProperty("user.dir");
        File directory = new File(property + File.separator + "questiondata");
        if (!directory.exists()) {
            directory.mkdirs();
        }
        String filePath = directory.getAbsolutePath() + File.separator + "question_data.csv";

        try (FileWriter fileWriter = new FileWriter(filePath)) {
            // 写入数据行（不再写入文件头）
            for (QuestionRecomend recommendation : questionRecomendList) {
                StringBuilder line = new StringBuilder();
                // 假设 getUserId() 返回 Long 类型
                line.append(recommendation.getUserId()).append(",")
                        // 假设 getQuestionId() 返回 Long 类型
                        .append(recommendation.getQuestionId()).append(",")
                        // 假设 getStatus() 返回 int 或 String 类型
                        .append(recommendation.getStatus());
                fileWriter.write(line.toString());
                // 换行
                fileWriter.write("\n");
            }
            fileWriter.flush();
        } catch (IOException e) {
            log.error("写入csv文件失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件写入失败");
        }
        // 3、调用Mahout进行推荐
        try {
            // 本地csv文件数据模型
            DataModel dataModel = new FileDataModel(new File(filePath));
            // 计算相似度
            PearsonCorrelationSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
            // 计算最近邻域
            UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(100, similarity, dataModel);
            // 构建推荐器
            Recommender recommender = new GenericUserBasedRecommender(dataModel, userNeighborhood, similarity);
            // 给userId用户推荐2个题目
            List<RecommendedItem> recommendedItemList = recommender.recommend(userId, 2);

            for (RecommendedItem recommendation : recommendedItemList) {
                log.info("推荐项: {}", recommendation);
            }

            // 将推荐结果转换为业务需要的格式
            List<Integer> recommendedQuestionIds = new ArrayList<>();
            for (RecommendedItem item : recommendedItemList) {
                recommendedQuestionIds.add((int) item.getItemID());
            }
            // 4、将推荐结果返回
            return recommendedQuestionIds;
        } catch (Exception e) {
            log.error("Mahout 推荐过程中出现错误: {}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 从数据库中根据推荐的题目id去查询相关题目信息
     * @param userId
     * @return
     */
    public List<Question> doRecommend(Long userId){
        List<Integer> recommendList = getRecommendId(userId);
        if (recommendList == null || recommendList.size() == 0){
            return null;
        }
        // 根据推荐生成的题目id再去数据库中查询相关题目
        List<Question> questions = questionMapper.selectBatchIds(recommendList);
        return questions;
    }
}
